package com.winning.pmph.service;

import cn.hutool.json.XML;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.winning.pmph.entity.*;
import com.winning.pmph.mapper.*;
import com.winning.pmph.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 刘亚飞
 * @Description
 * @create 2021-08-12 16:26
 */
@Slf4j
@Component
@Transactional
public class JournalService extends ServiceImpl<JournalMapper, Journal> {

    @Resource
    private JournalMapper journalMapper;

    @Resource
    private JournalArticleMapper journalArticleMapper;

    @Resource
    private UncommonWordService uncommonWordService;

    @Resource
    private KnowledgeSyncRecordMapper knowledgeSyncRecordMapper;

    @Resource
    private KnowledgeSyncStatusMapper knowledgeSyncStatusMapper;

    @Resource
    private PlatformKnowledgeLibMapper platformKnowledgeLibMapper;

    @Resource
    private KnowledgeSyncStatusService knowledgeSyncStatusService;
    @Resource
    private DataPermissionService dataPermissionService;

    @Transactional
    public void uploadAndParse(Journal journal, String header) {
        // 解压zip包;
        ParseBookZipUtil.unzipBook(journal.getFilePath(), header);
        // 递归查找某一文件夹下同时含有manifest.xml,images 2个文件的文件夹，这个文件夹下的
        // 文件才是正确的应该被解析的文件
        List<String> fileList = Lists.newArrayList("manifest.xml", "images");
        String analysisPath = ParseBookZipUtil.recursiveFindFile(new File(StringUtils.removeEnd(journal.getFilePath(), ".zip")), fileList);
        if (StringUtils.isBlank(analysisPath)) {
            throw new RuntimeException("未查找到同时包含manifest.xml，images的文件夹");
        }
        journal.setFilePath(analysisPath);
        journal.setImageList(Arrays.asList(new File(analysisPath + "/images").list()));
        List<UncommonWord> uncommonWordList = uncommonWordService.list();
        Map<String, UncommonWord> uncommonWordMap = uncommonWordList.stream()
                .filter(item -> StringUtils.isNotEmpty(item.getCode()))
                .collect(Collectors.toMap(item -> item.getCode(), item -> item, (a, b) -> a));
        journal.setUncommonWordMap(uncommonWordMap);
        // 解析期刊元数据
        parseMetadata(journal);
        // 解析文章对应的xml文件
        parseArticle(journal);
        // 移动文件夹
        try {
            // 把图片统一存储到这个路径下
            String targetDirPath = ApplicationContextUtil.BASE_DOWN_FORDER + "image/journal/" + journal.getImgPath();
            FileUtils.deleteDirectory(new File(targetDirPath));
            FileUtils.copyDirectory(new File(journal.getFilePath()), new File(targetDirPath));
        } catch (Exception e) {
            log.error("移动图片文件夹异常：", e);
            throw new RuntimeException("移动图片文件夹异常：" + e.toString());
        }
        journal.setStatus(Journal.STATUS_IMPORT_SUCCESS);
    }

    public Page<Journal> queryList(Page<Journal> page, Journal journal) {
        journal.setJournalPermissionList(dataPermissionService.getCrtUserJournalPermission());
        return journalMapper.queryList(page, journal);
    }

    public void syncToPlatform(Journal journal, String userName, String option) {
        Arrays.asList(journal.getId().split(",")).forEach(id -> {
            // 先同步元数据信息;
            Journal syncJournal = journalMapper.selectById(id);
            syncJournal.setSelectedPlatform(journal.getSelectedPlatform());
            JSONObject data = JSON.parseObject(syncJournal.getMetadata());
            data.put("id", syncJournal.getId());
            data.put("version", syncJournal.getVersion());
            JSONObject journalMetadataObject = new JSONObject();
            journalMetadataObject.put("type", Const.JOURNAL_METADATA);
            journalMetadataObject.put("option", option);
            journalMetadataObject.put("data", data);
            boolean sendFlag = RocketMQProducerUtil.sendMessage(syncJournal.getSelectedPlatform(), Const.JOURNAL_METADATA, syncJournal.getName(), journalMetadataObject.toJSONString());
            // 如果元数据推送成功,再推送期刊目录
            if (!sendFlag) {
                insertSync(syncJournal, userName, sendFlag);
                syncJournal.setUpdatedBy(userName);
                syncJournal.setUpdatedTime(LocalDateTimeUtil.getNowAsString());
                journalMapper.updateById(syncJournal);
                return;
            }
            // 同步期刊目录;
            JSONObject catalogData = JSON.parseObject(syncJournal.getCatalog());
            catalogData.put("id", syncJournal.getId());
            catalogData.put("version", syncJournal.getVersion());
            // 为和文章的同步目录格式保持一致
            catalogData.put("catalog", catalogData.getJSONArray("subject-group"));
            catalogData.remove("subject-group");
            JSONObject journalCatalogObject = new JSONObject();
            journalCatalogObject.put("type", Const.JOURNAL_CATALOG);
            journalCatalogObject.put("option", option);
            journalCatalogObject.put("data", catalogData);
            sendFlag = RocketMQProducerUtil.sendMessage(syncJournal.getSelectedPlatform(), Const.JOURNAL_CATALOG, syncJournal.getName(), journalCatalogObject.toJSONString());
            if (!sendFlag) {
                insertSync(syncJournal, userName, sendFlag);
                syncJournal.setUpdatedBy(userName);
                syncJournal.setUpdatedTime(LocalDateTimeUtil.getNowAsString());
                journalMapper.updateById(syncJournal);
                return;
            }
            List<JournalArticle> articleList = journalArticleMapper.selectBeansByWherePart(" journal_id = '" + id + "' order by id");
            for (int i = 0; i < articleList.size(); i++) {
                JournalArticle journalArticle = articleList.get(i);
                JSONObject articleMetadataData = JSON.parseObject(journalArticle.getMetadata());
                articleMetadataData.put("id", journalArticle.getId());
                articleMetadataData.put("journalId", syncJournal.getId());
                articleMetadataData.put("version", syncJournal.getVersion());
                JSONObject journalArticleMetadataObject = new JSONObject();
                journalArticleMetadataObject.put("type", Const.JOURNAL_ARTICLE_METADATA);
                journalArticleMetadataObject.put("option", option);
                journalArticleMetadataObject.put("data", articleMetadataData);
                // 同步期刊文章元数据
                sendFlag = RocketMQProducerUtil.sendMessage(syncJournal.getSelectedPlatform(), Const.JOURNAL_ARTICLE_METADATA, syncJournal.getName(), journalArticleMetadataObject.toJSONString());
                if (!sendFlag) {
                    insertSync(syncJournal, userName, sendFlag);
                    syncJournal.setUpdatedBy(userName);
                    syncJournal.setUpdatedTime(LocalDateTimeUtil.getNowAsString());
                    journalMapper.updateById(syncJournal);
                    return;
                }
                // 同步期刊文章目录
                JSONObject articleCatalogData = new JSONObject();
                articleCatalogData.put("id", journalArticle.getId());
                articleCatalogData.put("journalId", syncJournal.getId());
                articleCatalogData.put("version", syncJournal.getVersion());
                articleCatalogData.put("catalog", JSON.parseArray(journalArticle.getCatalog()));
                JSONObject journalArticleCatalogObject = new JSONObject();
                journalArticleCatalogObject.put("type", Const.JOURNAL_ARTICLE_CATALOG);
                journalArticleCatalogObject.put("option", option);
                journalArticleCatalogObject.put("data", articleCatalogData);
                sendFlag = RocketMQProducerUtil.sendMessage(syncJournal.getSelectedPlatform(), Const.JOURNAL_ARTICLE_CATALOG, syncJournal.getName(), journalArticleCatalogObject.toJSONString());
                if (!sendFlag) {
                    insertSync(syncJournal, userName, sendFlag);
                    syncJournal.setUpdatedBy(userName);
                    syncJournal.setUpdatedTime(LocalDateTimeUtil.getNowAsString());
                    journalMapper.updateById(syncJournal);
                    return;
                }
                // 同步期刊文章
                JSONObject articleContentData = new JSONObject();
                articleContentData.put("id", journalArticle.getId());
                articleContentData.put("pdfPath", journalArticle.getPdfPath());
                articleContentData.put("journalId", syncJournal.getId());
                articleContentData.put("version", syncJournal.getVersion());
                articleContentData.put("content", journalArticle.getBody());
                JSONObject journalArticleContentObject = new JSONObject();
                journalArticleContentObject.put("type", Const.JOURNAL_ARTICLE_CONTENT);
                journalArticleContentObject.put("option", option);
                journalArticleContentObject.put("data", articleContentData);
                sendFlag = RocketMQProducerUtil.sendMessage(syncJournal.getSelectedPlatform(), Const.JOURNAL_ARTICLE_CONTENT, syncJournal.getName(), journalArticleContentObject.toJSONString());
                if (!sendFlag) {
                    insertSync(syncJournal, userName, sendFlag);
                    syncJournal.setUpdatedBy(userName);
                    syncJournal.setUpdatedTime(LocalDateTimeUtil.getNowAsString());
                    journalMapper.updateById(syncJournal);
                    return;
                }
            }
            insertSync(syncJournal, userName, sendFlag);
            // 同步成功;
            syncJournal.setUpdatedBy(userName);
            syncJournal.setUpdatedTime(LocalDateTimeUtil.getNowAsString());
            journalMapper.updateById(syncJournal);
        });
    }

    @Transactional
    public void deleteById(String id) {
        journalMapper.deleteById(id);
        journalArticleMapper.deleteBeansByWherePart(" journal_id = '" + id + "'");
    }

    /**
     * 准备知识同步日志对象, 同步状态对象;
     *
     * @param journal
     * @param userName
     * @param sendFlag
     * @return
     */
    private void insertSync(Journal journal, String userName, boolean sendFlag) {
        KnowledgeSyncRecord knowledgeSyncRecord = new KnowledgeSyncRecord();
        knowledgeSyncRecord.setKnowledgeId(journal.getId());
        knowledgeSyncRecord.setPlatformId(journal.getSelectedPlatform());
        knowledgeSyncRecord.setOperatedBy(userName);
        knowledgeSyncRecord.setVersion(journal.getVersion());
        knowledgeSyncRecord.setOperatedTime(LocalDateTimeUtil.getNowAsString());
        KnowledgeSyncStatus knowledgeSyncStatus = JSON.parseObject(JSON.toJSONString(knowledgeSyncRecord), KnowledgeSyncStatus.class);
        knowledgeSyncStatus.setKnowledgeLibId(journal.getIssn());
        // 改状态;
        if (sendFlag) {
            knowledgeSyncRecord.setSyncFlag(KnowledgeSyncRecord.SYNC_STATUS_SUCCESS);
            knowledgeSyncStatus.setKind(KnowledgeSyncStatus.KIND_SUCCESS);
        } else {
            knowledgeSyncRecord.setSyncFlag(KnowledgeSyncRecord.SYNC_STATUS_FAILURE);
            knowledgeSyncStatus.setKind(KnowledgeSyncStatus.FAILED_KIND_PRODUCER);
        }
        // 记录同步日志, 同步状态;
        knowledgeSyncRecordMapper.saveBean(knowledgeSyncRecord);
        // 先删除再新增;
        knowledgeSyncStatusMapper.deleteBeansByWherePart(
                "knowledge_id = '" + knowledgeSyncStatus.getKnowledgeId() +
                        "' and platform_id = '" + knowledgeSyncStatus.getPlatformId() + "'");
        knowledgeSyncStatusMapper.saveBean(knowledgeSyncStatus);
    }


    /**
     * 解析期刊元数据信息
     *
     * @param journal
     */
    private void parseMetadata(Journal journal) {
        // 解析期刊元数据
        String metadataStr = fileToString(journal.getFilePath(), "manifest.xml", journal.getUncommonWordMap());
        // 解析元数据中的文章目录,看是否和文件夹中xml文件的数据量对应上
        List<String> xmlArticleList = Jsoup.parse(metadataStr).getElementsByTag("file").stream().map(t -> t.attr("src")).filter(t -> t.endsWith(".xml")).collect(Collectors.toList());
        File articleFilePath = new File(journal.getFilePath());
        journal.setArticleFile(Arrays.asList(articleFilePath.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().endsWith(".xml") && !"manifest.xml".equals(pathname.getName());
            }
        })));
        List<String> zipArticleList = journal.getArticleFile().stream().map(File::getName).collect(Collectors.toList());
        xmlArticleList.sort(Comparator.comparing(String::hashCode));
        zipArticleList.sort(Comparator.comparing(String::hashCode));
        if (!xmlArticleList.toString().equals(zipArticleList.toString())) {
            throw new RuntimeException("manifest.xml文件中文章目录数量与zip包中文章的xml文件数量对应不上");
        }
        // 判断PDF文件是否缺失
        journal.getArticleFile().forEach(xmlFileItem -> {
            String pdfFileName = StringUtils.replace(xmlFileItem.getName(), ".xml", ".pdf");
            if (!new File(journal.getFilePath() + "/" + pdfFileName).exists()) {
                throw new RuntimeException("缺少文章pdf文件，缺少文件为：" + pdfFileName);
            }
        });
        Element body = Jsoup.parse(metadataStr).body();
        Element metadata = body.getElementsByTag("journal-meta").first();
        metadata.append(body.getElementsByTag("issue-meta").first().html());
        // 添加CDATA
        recursiveAddCdata(metadata);
        JSONObject metadataJson = JSON.parseObject(XML.toJSONObject(metadata.html()).toString());
        // 解析名称
        // 1,统一中英文顺序
        JSONArray titleJsonArray = new JSONArray();
        JSONObject titleObject = metadataJson.getJSONObject("journal-title-group");
        if (titleObject.get("journal-title") instanceof String) {
            // 只有中文,无英文  已确认过,所有的期刊都是中文期刊
            journal.setName(titleObject.get("journal-title").toString());
            titleJsonArray.add(journal.getName());
        } else {
            // 中英文名称都有
            titleObject.getJSONArray("journal-title").stream()
                    // 中英文名排序, 中文名为字符串, 英文名为对象, 中文名在前;
                    .sorted((a, b) -> (a instanceof String) ? -1 : 1)
                    .forEach(titleJsonArray::add);
            journal.setName(titleJsonArray.getString(0));
        }
        titleObject.put("journal-title", titleJsonArray);
        // 统一数据格式
        // 2,ISSN节点是个数组,包含cn号和issn号,保证第一条是issn号,第二条是cn号
        JSONArray issnArray = new JSONArray();
        JSONArray issnJsonArray = metadataJson.getJSONArray("issn");
        issnJsonArray.stream().forEach(t -> {
            String pubType = ((JSONObject) t).getString("pub-type");
            if (StringUtils.equals("ppub", pubType)) {
                issnArray.add(t);
                journal.setIssn(((JSONObject) t).getString("content"));
            }
        });
        issnJsonArray.stream().forEach(t -> {
            String pubType = ((JSONObject) t).getString("pub-type");
            if (StringUtils.equals("cn", pubType)) {
                issnArray.add(t);
                journal.setCn(((JSONObject) t).getString("content"));
            }
        });
        metadataJson.put("issn", issnArray);
        // 3,journal-id节点是个数组,保证下面这个顺序
        /*<!-- 在Pubmed数据库中的名称-->
			<journal-id journal-id-type="nlm-ta"/>
			<!-- 出版商赋予期刊ID-->
			<journal-id journal-id-type="publisher-id"/>
			<!-- 在PMC数据库中的名称-->
			<journal-id journal-id-type="pmc"/>
         */
        JSONArray journalIdArray = new JSONArray();
        JSONArray journalIdArrayTemp = metadataJson.getJSONArray("journal-id");
        journalIdArrayTemp.stream().forEach(t -> {
            String pubType = ((JSONObject) t).getString("journal-id-type");
            if (StringUtils.equals("nlm-ta", pubType)) {
                journalIdArray.add(t);
            }
        });
        journalIdArrayTemp.stream().forEach(t -> {
            String pubType = ((JSONObject) t).getString("journal-id-type");
            if (StringUtils.equals("publisher-id", pubType)) {
                journalIdArray.add(t);
            }
        });
        journalIdArrayTemp.stream().forEach(t -> {
            String pubType = ((JSONObject) t).getString("journal-id-type");
            if (StringUtils.equals("pmc", pubType)) {
                journalIdArray.add(t);
            }
        });
        metadataJson.put("journal-id", journalIdArray);
        // 4,出版商确定顺序
        JSONArray publishArray = new JSONArray();
        JSONObject publisherObject = metadataJson.getJSONObject("publisher");
        Object nameObject = publisherObject.get("publisher-name");
        JSONArray locArray = new JSONArray();
        // 只有中文,且没有主办单位
        if (nameObject instanceof String) {
            throw new RuntimeException("缺少主办单位");
        }
        Optional<Object> optional = ((JSONArray) nameObject).stream().filter(t -> (t instanceof JSONObject) && StringUtils.isNotBlank(((JSONObject) t).getString("specific-use"))).findAny();
        if (!optional.isPresent()) {
            throw new RuntimeException("缺少主办单位");
        }
        // 中英文名称都有,保证第一条是中文名称或者中文地址
        // 中文类型是字符串.英文是对象,出版单位也是对象,但是出版单位有个属性sponsor
        ((JSONArray) nameObject).forEach(t -> {
            if (t instanceof String) {
                publishArray.add(t);
            }
        });
        // 出版单位英文名称
        ((JSONArray) nameObject).forEach(t -> {
            if ((t instanceof JSONObject) && StringUtils.isBlank(((JSONObject) t).getString("specific-use"))) {
                publishArray.add(t);
            }
        });
        // 主办单位中文名称
        ((JSONArray) nameObject).forEach(t -> {
            if ((t instanceof JSONObject) && StringUtils.isNotBlank(((JSONObject) t).getString("specific-use"))
                    && StringUtils.isBlank(((JSONObject) t).getString("xml:lang"))) {
                publishArray.add(t);
            }
        });
        // 主办单位英文名称
        ((JSONArray) nameObject).forEach(t -> {
            if ((t instanceof JSONObject) && StringUtils.isNotBlank(((JSONObject) t).getString("specific-use"))
                    && StringUtils.isNotBlank(((JSONObject) t).getString("xml:lang"))) {
                publishArray.add(t);
            }
        });
        // 地址
        JSONArray jsonArray1 = publisherObject.getJSONArray("publisher-loc");
        jsonArray1.forEach(t -> {
            if (t instanceof String) {
                locArray.add(t);
            }
        });
        // 出版单位英文地址
        jsonArray1.forEach(t -> {
            if ((t instanceof JSONObject) && StringUtils.isBlank(((JSONObject) t).getString("specific-use"))) {
                locArray.add(t);
            }
        });
        // 主办单位中文名称
        jsonArray1.forEach(t -> {
            if ((t instanceof JSONObject) && StringUtils.isNotBlank(((JSONObject) t).getString("specific-use"))
                    && StringUtils.isBlank(((JSONObject) t).getString("xml:lang"))) {
                locArray.add(t);
            }
        });
        // 主办单位英文名称
        jsonArray1.forEach(t -> {
            if ((t instanceof JSONObject) && StringUtils.isNotBlank(((JSONObject) t).getString("specific-use"))
                    && StringUtils.isNotBlank(((JSONObject) t).getString("xml:lang"))) {
                locArray.add(t);
            }
        });
        publisherObject.put("publisher-name", publishArray);
        publisherObject.put("publisher-loc", locArray);
        journal.setVolume(metadataJson.getString("volume"));
        journal.setIssue(metadataJson.getString("issue"));
        // 出版年份
        journal.setPublishYear(metadataJson.getJSONObject("pub-date").getString("year"));
        // 判断是否有同一卷,同一期的期刊已经导入
        List<Journal> journalList = journalMapper.selectBeansByWherePart(" issn = '" + journal.getIssn() + "' " +
                " and volume = '" + journal.getVolume() + "' and issue = '" + journal.getIssue() + "' and id != '" + journal.getId() + "'");
        if (journalList.size() > 0) {
            throw new RuntimeException("ISSN号+卷号+期号重复");
        }
        journal.setImgPath(journal.getIssn() + "/" + journal.getVolume() + "/" + journal.getIssue());
        metadataJson.put("cover-img", "");
        body.getElementsByTag("files").first().getElementsByTag("file").forEach(t -> {
            // 移除封面图file标签
            if (t.attr("src").contains("cover")) {
                String coverImg = t.attr("src");
                // 取得封面图文件名称
                coverImg = coverImg.substring(coverImg.lastIndexOf("/") + 1);
                String finalCoverImg = coverImg;
                List<File> coverImageList = Arrays.asList(new File(journal.getFilePath()).listFiles(new FileFilter() {
                    @Override
                    public boolean accept(File pathname) {
                        return pathname.getName().equals(finalCoverImg);
                    }
                }));
                if (CollectionUtils.isEmpty(coverImageList)) {
                    throw new RuntimeException("封面图【" + coverImg + "】缺失");
                }
                metadataJson.put("cover-img", "/ymer/action/file/showImage?filename=image/journal/" + journal.getImgPath() + "/" + coverImg);
                t.remove();
            } else if (t.attr("src").contains("content")) {
                // 移除目录的file标签
                t.remove();
            } else {
                t.attr("articleId", t.attr("src")).removeAttr("src");
            }
        });
        journal.setMetadata(metadataJson.toString());
        journal.setCatalog(body.getElementsByTag("files").html());
    }

    /**
     * 解析期刊对应的文章
     *
     * @param journal
     */
    private void parseArticle(Journal journal) {
        Element body = Jsoup.parse(journal.getCatalog()).body();
        List<File> articleFileList = journal.getArticleFile();
        articleFileList.forEach(file -> {
            log.info("期刊名:" + journal.getName() + ",期刊文章名:" + file.getName());
            System.out.println("期刊名:" + journal.getName() + ",期刊文章名:" + file.getName());
            JournalArticle journalArticle = new JournalArticle();
            journalArticle.setJournalId(journal.getId());
            // 存储pdf对应的路径
            journalArticle.setPdfPath("/pmph/action/file/getFile?filename=image/journal/"
                    + journal.getImgPath() + "/" + StringUtils.replace(file.getName(), ".xml", ".pdf"));
            // 解析期刊文章对应的内容
            String articleStr = fileToString(journal.getFilePath(), file.getName(), journal.getUncommonWordMap());
            Element htmlBody = Jsoup.parse(articleStr).body();
            Element articleMetadataEle = htmlBody.getElementsByTag("article-meta").first();
            // 解析文章元数据
            recursiveAddCdata(articleMetadataEle);
            cn.hutool.json.JSONObject jsonObject = XML.toJSONObject(articleMetadataEle.html());
            // 1,规范作者单位格式
            JSONObject fastJson = JSON.parseObject(jsonObject.toString());
            Object affAlterObject = fastJson.get("aff-alternatives");
            if (Objects.nonNull(affAlterObject)) {
                JSONArray tempaffJsonArray = new JSONArray();
                // 只有一个作者单位,转为数组,方便统一处理
                if (affAlterObject instanceof JSONObject) {
                    tempaffJsonArray.add(affAlterObject);
                    fastJson.put("aff-alternatives", tempaffJsonArray);
                }
                // 说明有多个作者,数组
                fastJson.getJSONArray("aff-alternatives").forEach(t1 -> {
                    Object affObject = ((JSONObject) t1).get("aff");
                    if (affObject instanceof String) {
                        JSONArray affArray = new JSONArray();
                        // 说明只有中文
                        affArray.add(affObject);
                        ((JSONObject) t1).put("aff", affArray);
                    }
                    // 中英文都有,保证中文在第一位
                    ((JSONObject) t1).getJSONArray("aff").stream()
                            // 中英文名排序, 中文名为字符串, 英文名为对象, 中文名在前;
                            .sorted((a, b) -> (a instanceof String) ? -1 : 1);
                });
            } else {
                // 作者单位只有中文,没有英文,标签是aff
                Object affObject = fastJson.get("aff");
                // 如果存在,组装成 aff-alternatives 标签的格式
                if (Objects.nonNull(affObject)) {
                    if (affObject instanceof JSONObject) {
                        JSONArray tempaffArray = new JSONArray();
                        tempaffArray.add(affObject);
                        fastJson.put("aff", tempaffArray);
                    }
                    JSONArray affAlterArray = new JSONArray();
                    fastJson.getJSONArray("aff").forEach(t -> {
                        JSONObject temp = new JSONObject();
                        // 强转
                        JSONObject tempAffObject = ((JSONObject) t);
                        temp.put("id", tempAffObject.getString("id"));
                        tempAffObject.remove("id");
                        JSONArray affArray = new JSONArray();
                        affArray.add(tempAffObject.getString("content"));
                        temp.put("aff", affArray);
                        affAlterArray.add(temp);
                    });
                    fastJson.remove("aff");
                    fastJson.put("aff-alternatives", affAlterArray);
                }
            }

            // 3,规范目录格式
            Object subjObject = fastJson.getJSONObject("article-categories").get("subj-group");
            JSONArray subjArray = new JSONArray();
            if (subjObject instanceof JSONObject) {
                // 说明只有中文目录
                subjArray.add(subjObject);
            } else {
                subjArray = ((JSONArray) subjObject);
            }
            // 调序，保证栏目的中文名称在第一位
            // todo 代码待优化
            JSONArray afterSort = new JSONArray();
            subjArray.forEach(subjItem -> {
                JSONObject subjItemObject = (JSONObject) subjItem;
                String groupType = subjItemObject.getString("subj-group-type");
                String xmlLang = subjItemObject.getString("xml:lang");
                if ("heading".equals(groupType) && StringUtils.isBlank(xmlLang)) {
                    afterSort.add(subjItem);
                }
            });

            subjArray.forEach(subjItem -> {
                JSONObject subjItemObject = (JSONObject) subjItem;
                String groupType = subjItemObject.getString("subj-group-type");
                String xmlLang = subjItemObject.getString("xml:lang");
                if ("heading".equals(groupType) && "en".equals(xmlLang)) {
                    afterSort.add(subjItem);
                }
            });

            subjArray.forEach(subjItem -> {
                JSONObject subjItemObject = (JSONObject) subjItem;
                String groupType = subjItemObject.getString("subj-group-type");
                if ("clc".equals(groupType)) {
                    afterSort.add(subjItem);
                }
            });

            subjArray.forEach(subjItem -> {
                JSONObject subjItemObject = (JSONObject) subjItem;
                String groupType = subjItemObject.getString("subj-group-type");
                if ("dc".equals(groupType)) {
                    afterSort.add(subjItem);
                }
            });

            subjArray.forEach(subjItem -> {
                JSONObject subjItemObject = (JSONObject) subjItem;
                String groupType = subjItemObject.getString("subj-group-type");
                if ("loe".equals(groupType)) {
                    afterSort.add(subjItem);
                }
            });

            subjArray.forEach(subjItem -> {
                JSONObject subjItemObject = (JSONObject) subjItem;
                String groupType = subjItemObject.getString("subj-group-type");
                if (!"heading".equals(groupType) && !"clc".equals(groupType)
                        && !"dc".equals(groupType) && !"loe".equals(groupType)) {
                    afterSort.add(subjItem);
                }
            });

            if (subjArray.size() != afterSort.size()) {
                throw new RuntimeException("字段解析丢了");
            }

            fastJson.getJSONObject("article-categories").put("subj-group", afterSort);

            // 提取目录
            List<String> subjList = Lists.newArrayList();
            recursionSubj(afterSort.getJSONObject(0), subjList);
            journalArticle.setClassify(StringUtils.join(subjList, "->"));

            // 4,规范通信作者格式
            Object authorNotesObject = fastJson.get("author-notes");
            // 通信作者有可能为空
            if (Objects.nonNull(authorNotesObject)) {
                // 只有一个通信作者,转数组
                if (authorNotesObject instanceof JSONObject) {
                    JSONArray tempcorrespArray = new JSONArray();
                    tempcorrespArray.add(authorNotesObject);
                    fastJson.put("author-notes", tempcorrespArray);
                }
                // 处理数组
                JSONArray jsonArray2 = fastJson.getJSONArray("author-notes");
                jsonArray2.forEach(t -> {
                    Object correspObject = ((JSONObject) t).get("corresp");
                    // 没有通讯作者
                    if (Objects.isNull(correspObject)) {
                        return;
                    }
                    // 只有中文
                    if (correspObject instanceof JSONObject) {
                        JSONArray correspArray = new JSONArray();
                        correspArray.add(correspObject);
                        ((JSONObject) t).put("corresp", correspArray);
                    } else {
                        ((JSONArray) correspObject).stream()
                                // 中英文名排序, 中文名为字符串, 英文名为对象, 中文名在前;
                                .sorted((a, b) -> (StringUtils.isBlank(((JSONObject) a).getString("xml:lang"))) ? -1 : 1);
                    }
                });
            }

            // 5,规范文章作者,文章编者格式
            Object contribGroupObject = fastJson.get("contrib-group");
            if (Objects.nonNull(contribGroupObject)) {
                // 说明只有文章作者或者文章编者,把对象转为数组
                if (contribGroupObject instanceof JSONObject) {
                    JSONArray tempcontribGroupArray = new JSONArray();
                    tempcontribGroupArray.add(contribGroupObject);
                    fastJson.put("contrib-group", tempcontribGroupArray);
                }

                // 用来存储文章作者信息
                List<String> articleAuthorList = Lists.newArrayList();
                fastJson.getJSONArray("contrib-group").forEach(t -> {
                    Object contribObject = ((JSONObject) t).get("contrib");
                    // 判断文章作者,说明只有一个作者,转为数组
                    if (contribObject instanceof JSONObject) {
                        JSONArray tempcontribGroupArray = new JSONArray();
                        tempcontribGroupArray.add(contribObject);
                        ((JSONObject) t).put("contrib", tempcontribGroupArray);
                    }
                });

                // 保证作者和编者顺序
                fastJson.getJSONArray("contrib-group").stream()
                        // 作者在前,编者在后
                        .sorted((a, b) -> (StringUtils.equals(((JSONObject) a).getJSONArray("contrib").getJSONObject(0).getString("contrib-type"), "author")) ? -1 : 1);

                fastJson.getJSONArray("contrib-group").forEach(t -> {
                    ((JSONObject) t).getJSONArray("contrib").forEach(t1 -> {
                        JSONObject t1Object = ((JSONObject) t1);
                        if ("author".equals(t1Object.getString("contrib-type"))) {
                            JSONObject alterObject = t1Object.getJSONObject("name-alternatives");
                            Object nameObject = null;
                            if (Objects.nonNull(alterObject)) {
                                nameObject = alterObject.get("name");
                                // 只有中文名
                                if (nameObject instanceof JSONObject) {
                                    JSONArray nameArray = new JSONArray();
                                    nameArray.add(nameObject);
                                    alterObject.put("name", nameArray);
                                } else {
                                    ((JSONArray) nameObject).stream()
                                            // 中英文名排序, 中文名为字符串, 英文名为对象, 中文名在前;
                                            .sorted((a, b) -> (StringUtils.isBlank(((JSONObject) a).getString("xml:lang"))) ? -1 : 1);
                                }
                                alterObject.getJSONArray("name").forEach(t2 -> {
                                    if (StringUtils.isBlank(((JSONObject) t2).getString("xml:lang"))) {
                                        articleAuthorList.add((((JSONObject) t2).getString("surname")) + (((JSONObject) t2).getString("given-names")));
                                    }
                                });
                            } else if (Objects.nonNull(t1Object.getJSONObject("name"))) {
                                JSONArray tempNameArray = new JSONArray();
                                tempNameArray.add(t1Object.getJSONObject("name"));
                                t1Object.put("name-alternatives", tempNameArray);
                                articleAuthorList.add(t1Object.getJSONObject("name").getString("surname") + t1Object.getJSONObject("name").getString("given-names"));
                                t1Object.remove("name");
                            } else if (Objects.nonNull(t1Object.getJSONObject("collab-alternatives"))) {
                                alterObject = t1Object.getJSONObject("collab-alternatives");
                                nameObject = alterObject.get("collab");
                                // 只有中文名
                                if (nameObject instanceof String) {
                                    JSONArray nameArray = new JSONArray();
                                    nameArray.add(nameObject);
                                    alterObject.put("collab", nameArray);
                                } else {
                                    ((JSONArray) nameObject).stream()
                                            // 中英文名排序, 中文名为字符串, 英文名为对象, 中文名在前;
                                            .sorted((a, b) -> (a instanceof String) ? -1 : 1);
                                }
                                alterObject.getJSONArray("collab").forEach(t2 -> {
                                    if (t2 instanceof String) {
                                        articleAuthorList.add(t2.toString());
                                    }
                                });
                            } else if (StringUtils.isNotBlank(t1Object.getString("collab"))) {
                                JSONArray tempNameArray = new JSONArray();
                                tempNameArray.add(t1Object.getString("collab"));
                                t1Object.put("collab-alternatives", tempNameArray);
                                articleAuthorList.add(t1Object.getString("collab"));
                                t1Object.remove("collab");
                            }
                        }
                    });
                });

                // 设置作者
                journalArticle.setAuthor(StringUtils.join(articleAuthorList, ";"));
            }

            // 6,规范基金内容格式
            Object fundingGroupObject = fastJson.get("funding-group");
            // 可能存在空的funding-group标签
            if (Objects.nonNull(fundingGroupObject) && StringUtils.isNotBlank(fundingGroupObject.toString())) {
                // 只有中文基金
                if (fundingGroupObject instanceof JSONObject) {
                    JSONArray tempawardArray = new JSONArray();
                    tempawardArray.add(fundingGroupObject);
                    fastJson.put("funding-group", tempawardArray);
                }
                fastJson.getJSONArray("funding-group").stream()
                        // 中英文名排序, 中文名为字符串, 英文名为对象, 中文名在前;
                        .sorted((a, b) -> (StringUtils.isBlank(((JSONObject) a).getString("xml:lang"))) ? -1 : 1);
            }
            // 关键字
            List<String> kwdGroupList = Lists.newArrayList();
            // 7,规范关键字格式
            Object kwdGroupObject = fastJson.get("kwd-group");
            if (Objects.nonNull(kwdGroupObject)) {
                if (kwdGroupObject instanceof JSONObject) {
                    JSONArray kwdGroupArray = new JSONArray();
                    // 只有中文关键字
                    kwdGroupArray.add(kwdGroupObject);
                    fastJson.put("kwd-group", kwdGroupArray);
                }
                fastJson.getJSONArray("kwd-group").stream().sorted((a, b) -> (StringUtils.isBlank(((JSONObject) a).getString("xml:lang"))) ? -1 : 1);
                fastJson.getJSONArray("kwd-group").forEach(t -> {
                    JSONObject tempObject = ((JSONObject) t);
                    if (tempObject.get("kwd") instanceof String) {
                        kwdGroupList.add(tempObject.getString("kwd"));
                    } else {
                        kwdGroupList.add(StringUtils.join(((JSONObject) t).getJSONArray("kwd").stream().toArray(), ";"));
                    }
                });
                fastJson.remove("kwd-group");
            }

            // 8,规范版权信息格式
            JSONObject permissionsObject = fastJson.getJSONObject("permissions");
            Object copyRightObject = permissionsObject.get("copyright-holder");
            // 只有中文
            if (copyRightObject instanceof String) {
                JSONArray copyRightArray = new JSONArray();
                copyRightArray.add(copyRightObject);
                permissionsObject.put("copyright-holder", copyRightArray);
            }
            permissionsObject.getJSONArray("copyright-holder").stream()
                    .sorted((a, b) -> (StringUtils.isBlank(((JSONObject) a).getString("xml:lang"))) ? -1 : 1);

            Object licenseObject = permissionsObject.get("license");
            if (Objects.nonNull(licenseObject)) {
                if (licenseObject instanceof JSONObject) {
                    JSONArray licenseArray = new JSONArray();
                    licenseArray.add(licenseArray);
                    permissionsObject.put("license", licenseArray);
                }
                permissionsObject.getJSONArray("license").stream()
                        .sorted((a, b) -> (StringUtils.isBlank(((JSONObject) a).getString("xml:lang"))) ? -1 : 1);
            }

            journalArticle.setName(fastJson.getJSONObject("title-group").getString("article-title"));
            // 规范DOI格式,转为数组
            Object articleIdObject = fastJson.get("article-id");
            if (articleIdObject instanceof JSONObject) {
                JSONArray articleIdArray = new JSONArray();
                articleIdArray.add(articleIdObject);
                fastJson.put("article-id", articleIdArray);
            }
            fastJson.getJSONArray("article-id").stream().sorted((a, b) -> "doi".equals(((JSONObject) a).getString("pub-id-type")) ? -1 : 1);
            journalArticle.setDoi(fastJson.getJSONArray("article-id").getJSONObject(0).getString("content"));

            // 规范收稿日期  可能没有
            if (Objects.nonNull(fastJson.getJSONObject("history"))) {
                Object dateObject = fastJson.getJSONObject("history").get("date");
                if (dateObject instanceof JSONObject) {
                    JSONArray tempDateArray = new JSONArray();
                    tempDateArray.add(dateObject);
                    fastJson.getJSONObject("history").put("date", tempDateArray);
                }
                // 排序,保证收稿日期在前
                fastJson.getJSONObject("history").getJSONArray("date").stream().sorted((a, b) -> "received".equals(((JSONObject) a).getString("date-type")) ? -1 : 1);
            }

            List<String> abstractList = Lists.newArrayList();
            if (Objects.nonNull(fastJson.getJSONObject("abstract"))) {
                abstractList.add(fastJson.getJSONObject("abstract").getString("content"));
                fastJson.remove("abstract");
            }
            if (Objects.nonNull(fastJson.getJSONObject("trans-abstract"))) {
                abstractList.add(fastJson.getJSONObject("trans-abstract").getString("content"));
                fastJson.remove("trans-abstract");
            }

            journalArticle.setMetadata(fastJson.toJSONString());
            htmlBody.getElementsByTag("front").remove();
            htmlBody.getElementsByTag("article").unwrap();
            htmlBody.getElementsByTag("back").unwrap();

            // 判断是否有多余的标签
            htmlBody.getAllElements().stream().map(ele -> ele.tagName()).collect(Collectors.toSet()).forEach(t -> {
                if (!Const.JOURNAL_TAG.contains(t)) {
                    throw new RuntimeException(file.getName() + "文件中含未知标签:" + t);
                }
            });
            // 拼接目录
            JSONArray jsonArray = new JSONArray();
            // 首先拼接关键词和摘要
            JSONObject catalogJson = new JSONObject();
            String kwdId = "CHP-kwd";
            catalogJson.put("id", kwdId);
            catalogJson.put("title", "关键词");
            jsonArray.add(catalogJson);
            catalogJson = new JSONObject();
            String abstractId = "CHP-abstract";
            catalogJson.put("id", abstractId);
            catalogJson.put("title", "摘要");
            jsonArray.add(catalogJson);

            htmlBody.prependElement("div").attr("id", abstractId).attr("style", "color: gray;").append("<h1 style=\"color:black;\">摘要</h1>").append(abstractList.size() > 0 ? StringUtils.join(abstractList, "") : "");
            htmlBody.prependElement("div").attr("id", kwdId).attr("style", "color: gray;").append("<h1 style=\"color:black;\">关键词</h1>")
                    .append(kwdGroupList.size() > 0 ? "<p>" + StringUtils.join(kwdGroupList, "</p><p>") + "</p>" : "");

            htmlBody.children().forEach(t -> {
                if (StringUtils.equals(t.tagName(), "sec")) {
                    recursionElement(jsonArray, t);
                }
                // 把参考文献拼接上去
                if (StringUtils.equals(t.tagName(), "ref-list")) {
                    JSONObject ref = new JSONObject();
                    String removeStartStr = StringUtils.removeStart(jsonArray.getJSONObject(jsonArray.size() - 1).getString("id"), "CHP");
                    String refId = "CHP" + (removeStartStr.equals("-abstract") ? 1 : Integer.valueOf(removeStartStr) + 1);
                    ref.put("id", refId);
                    ref.put("title", t.getElementsByTag("title").first().html());
                    t.tagName("div").attr("id", refId);
                    t.getElementsByTag("title").first().tagName("h1");
                    jsonArray.add(ref);
                }
            });
            journalArticle.setCatalog(jsonArray.toJSONString());
            // 解析body和back
            journalArticle.setBody(parseArticleBodyAndBack(file.getName(), htmlBody, journal));
            journalArticle.setCreatedBy(journal.getCreatedBy());
            journalArticle.setCreatedTime(journal.getCreatedTime());
            journalArticle.setUpdatedBy(journal.getUpdatedBy());
            journalArticle.setUpdatedTime(journal.getUpdatedTime());
            journalArticleMapper.saveBean(journalArticle);
            body.getElementsByAttributeValue("articleid", file.getName()).first().attr("articleid", journalArticle.getId());
        });
        recursiveAddCdata(body);
        JSONObject journalCatalog = JSON.parseObject(XML.toJSONObject(body.html()).toString());
        journalCatalog.getJSONArray("subject-group").forEach(t -> {
            transferJournalCatalog((JSONObject) t);
        });
        journal.setCatalog(journalCatalog.toJSONString());
    }

    private void transferJournalCatalog(JSONObject jsonObject) {
        if (Objects.nonNull(jsonObject.get("file"))) {
            if (jsonObject.get("file") instanceof JSONObject) {
                JSONArray fileArray = new JSONArray();
                fileArray.add(jsonObject.getJSONObject("file"));
                jsonObject.put("file", fileArray);
            }
            jsonObject.getJSONArray("file").forEach(t -> {
                JSONObject fileObject = ((JSONObject) t);
                Element titleEle = Jsoup.parse(fileObject.getString("content").replaceAll("<title>", "<mytitle>")
                        .replaceAll("</title>", "</mytitle>")).body();
                titleEle.getElementsByTag("mytitle").unwrap();
                fileObject.put("title", StringEscapeUtils.unescapeJava(titleEle.html()).trim());
                fileObject.remove("content");
            });
        }
        JSONObject subObject = jsonObject.getJSONObject("subject-group");
        if (Objects.nonNull(subObject)) {
            transferJournalCatalog(subObject);
        }
    }

    private void recursionSubj(JSONObject jsonObject, List<String> list) {
        list.add(jsonObject.getString("subject"));
        if (Objects.nonNull(jsonObject.getJSONObject("subj-group"))) {
            recursionSubj(jsonObject.getJSONObject("subj-group"), list);
        }
    }

    /**
     * 递归
     *
     * @param bodyE
     */
    private void recursionElement(JSONArray jsonArray, Element bodyE) {
        JSONObject catalogJson = new JSONObject();
        jsonArray.add(catalogJson);
        catalogJson.put("id", bodyE.attr("id"));
        Element titleFirst = Jsoup.parse(bodyE.getElementsByTag("title").first().html()).body();
        // 目录里标签转换
        titleFirst.getElementsByTag("xref").forEach(t -> {
            t.tagName("a").attr("href", "#" + t.attr("rid")).removeAttr("rid");
        });
        catalogJson.put("title", titleFirst.html());
        List<Element> elementList = bodyE.children().stream().filter(t -> StringUtils.equals("sec", t.tagName())).collect(Collectors.toList());
        if (elementList.size() > 0) {
            JSONArray sonArray = new JSONArray();
            elementList.forEach(element -> {
                if (StringUtils.equals("sec", element.tagName())) {
                    recursionElement(sonArray, element);
                }
            });
            catalogJson.put("children", sonArray);
        }
    }

    /**
     * 解析每篇文章的body和back部分
     *
     * @param htmlEle
     * @return
     */
    private String parseArticleBodyAndBack(String fileName, Element htmlEle, Journal journal) {
        // <xref ref-type="bibr" rid="R1">1</xref>  ->  <a href="#R1">1</a>
        // <title>1　宫颈癌的预防和筛查</title>  -> <h1>1　宫颈癌的预防和筛查</h1>
        htmlEle.getElementsByTag("title").forEach(t -> {
            if (StringUtils.equals("sec", t.parent().tagName())) {
                t.tagName("h" + t.parent().attr("id").split("-").length);
            }
        });
        Element bodyEle = Jsoup.parse(htmlEle.html()).body();
        bodyEle.getElementsByTag("xref").forEach(t -> {
            t.tagName("a").attr("href", "#" + t.attr("rid")).removeAttr("rid");
        });
        bodyEle.getElementsByTag("label").forEach(t -> {
            if (StringUtils.equals(t.parent().tagName(), "disp-formula")) {
                t.tagName("p").attr("class", "p_title");
            }
        });

        bodyEle.getElementsByTag("table-wrap").forEach(t -> {
            String str = "";
            for (Element child : t.children()) {
                if (StringUtils.equals("label", child.tagName())) {
                    str += child.html() + "  ";
                    child.remove();
                } else if (StringUtils.equals("p", child.tagName())) {
                    str += child.html();
                    child.remove();
                }
            }
            t.prependElement("p").attr("class", "t_title").text(str);
        });
        bodyEle.getElementsByTag("fig").forEach(t -> {
            String str = "";
            for (Element child : t.children()) {
                if (StringUtils.equals("label", child.tagName())) {
                    str += child.html() + "  ";
                    child.remove();
                } else if (StringUtils.equals("title", child.tagName())) {
                    str += child.html();
                    child.remove();
                } else if (StringUtils.equals("p", child.tagName()) && StringUtils.equals("fig_note", child.attr("content-type"))) {
                    child.attr("class", "p_note");
                }
            }
            t.prependElement("p").attr("class", "p_title").text(str);
        });
        bodyEle.getElementsByTag("fn").forEach(t -> {
            if (StringUtils.equals(t.parent().tagName(), "table-wrap-foot")) {
                t.tagName("p").attr("class", "t_note");
            } else if (StringUtils.equals(t.parent().tagName(), "fn-group")) {
                t.parent().unwrap();
                t.unwrap();
            }
        });

        // 去除所有的break标签
        bodyEle.getElementsByTag("break").unwrap();
        // 去除所有的list标签
        bodyEle.getElementsByTag("list").unwrap();
        // 去除所有的list-item标签
        bodyEle.getElementsByTag("list-item").unwrap();
        // 去除所有的ack标签
        bodyEle.getElementsByTag("ack").unwrap();
        // 处理response标签
        bodyEle.getElementsByTag("response").forEach(t -> {
            t.wrap("<div>");
        });
        // 处理underline标签
        bodyEle.getElementsByTag("underline").forEach(t -> {
            t.tagName("u");
        });
        // 处理strike标签
        bodyEle.getElementsByTag("strike").forEach(t -> {
            t.tagName("s");
        });
        bodyEle.getElementsByTag("response").forEach(t -> {
            t.getAllElements().unwrap();
        });
        // 改变标签
        Const.JOURNAL_TAG_TRANSFER_MAP.entrySet().forEach(entry -> {
            bodyEle.getElementsByTag(entry.getKey()).tagName(entry.getValue());
        });
        // 校验图片
        Element body = Jsoup.parse(bodyEle.html().replaceAll("&lt;", "<").replaceAll("&gt;", ">")).body();
        body.getElementsByTag("img").forEach(t -> {
            String imageSrc = t.attr("src");
            if (StringUtils.isBlank(imageSrc)) {
                throw new RuntimeException("【" + fileName + "】文件中xlink:href属性为空");
            }
            if (!"rw_img bz".equals(t.attr("class"))) {
                String imageName = imageSrc.substring(t.attr("src").lastIndexOf("/") + 1);
                // 判断图片是否在images文件夹里
                if (!journal.getImageList().contains(imageName)) {
                    throw new RuntimeException("图片【" + imageName + "】在images文件夹里找不到");
                }
                t.attr("src", "/ymer/action/file/showImage?filename=image/journal/" + journal.getImgPath() + "/images/" + imageName);
            }
        });
        return body.html();
    }

    /**
     * 递归添加CDATA标签
     *
     * @param element
     */
    private void recursiveAddCdata(Element element) {
        if (element.children().size() == 0) {
            element.html(StringUtils.isBlank(element.html()) ? "" : "<![CDATA[" + element.html() + "]]>");
        } else {
            boolean allMatch = element.children().stream()
                    .map(Element::tagName)
                    .allMatch(Const.HTML_TAGS::contains);
            if (allMatch) {
                element.html("<![CDATA[" + element.html() + "]]>");
            } else {
                element.children().forEach(t -> {
                    recursiveAddCdata(t);
                });
            }
        }
    }

    /**
     * 把文件中的内容转为字符串
     *
     * @param filePath
     * @param fileName
     * @return
     */
    private String fileToString(String filePath, String fileName,
                                Map<String, UncommonWord> uncommonWordMap) {
        try {
            File metadataFile = new File(filePath + "/" + fileName);
            // 生僻字转换
            return handleUncommonWord(FileUtils.readFileToString(metadataFile, "utf-8"), uncommonWordMap);
        } catch (IOException e) {
            log.error("解析" + fileName + "时发生错误：", e);
            throw new RuntimeException("解析" + fileName + "时发生错误：" + e.toString());
        }
    }

    /**
     * 处理生僻字
     *
     * @param value
     * @param uncommonWordMap
     * @return
     */
    private String handleUncommonWord(String value, Map<String, UncommonWord> uncommonWordMap) {
        // 因jsoup不识别 title标签
        value = value.replaceAll("<title>", "<mytitle>")
                .replaceAll("</title>", "</mytitle>")
                .replaceAll("<graphic", "<img").replaceAll("</graphic>", "")
                .replaceAll("<inline-graphic", "<img").replaceAll("</inline-graphic>", "")
                .replaceAll("xlink:href", "src")
                .replaceAll("<inline-formula>", "").replaceAll("</inline-formula>", "")
                .replaceAll("<inline-fourmula>", "").replaceAll("</inline-fourmula>", "");
        Element body = Jsoup.parse(value).body();
        body.getElementsByTag("bz").forEach(bz -> {
            String id = bz.attr("id");
            UncommonWord uncommonWord = uncommonWordMap.get(id);
            if (Objects.isNull(uncommonWord)) {
                throw new RuntimeException("未知的生僻字编码:" + id);
            }
            // 因img标签未自闭,导致后面转Jsonobject时报错,转为jsonobject后,再把myimg转为img
            bz.tagName("img").attr("data-similar-word", uncommonWord.getSimilarWord())
                    .attr("data-bz-id", uncommonWord.getId()).attr("title", uncommonWord.getSpellCode())
                    .attr("class", "rw_img bz").attr("src", uncommonWord.getPath()).removeAttr("id");
        });
        return body.html().replaceAll("<mytitle>", "<title>")
                .replaceAll("</mytitle>", "</title>");
    }

    public List<Journal> getJournalLibs() {
        return lambdaQuery().select(Journal::getName, Journal::getIssn).groupBy(Journal::getIssn,Journal::getName).list();
    }

    /**
     * 取得正在导入的列表
     *
     * @return
     */
    public List<Journal> getImportingJournal() {
        return lambdaQuery().select(Journal::getId).eq(Journal::getStatus, Journal.STATUS_IMPORTING).list();
    }

    public JournalArticle getArticleById(String id) {
        return journalArticleMapper.selectById(id);
    }

    public void updateJournalByJournal(Journal journal) {
        journal.setStatus(Journal.STATUS_IMPORT_SUCCESS);
        journal.setType(Journal.TYPE_OFFICIAL);
        journal.setVersion(journal.getVersion() + 1);
        journal.setReleaseTime(LocalDateTimeUtil.getNowAsString());
        journalMapper.updateById(journal);
        Map<String, UncommonWord> uncommonWordMap = uncommonWordService.list().stream().collect(Collectors.toMap(UncommonWord::getId, Function.identity()));
        // 修改同步状态;
        List<PlatformKnowledgeLib> platformKnowledgeLibs = platformKnowledgeLibMapper.lambdaQuery().eq(PlatformKnowledgeLib::getKnowledgeLibId, journal.getIssn()).list();
        for (PlatformKnowledgeLib platformKnowledgeLib : platformKnowledgeLibs) {
            // 如果自动同步, 则立即同步;
            if (platformKnowledgeLib.isAutosyncFlag()) {
                journal.setSelectedPlatform(platformKnowledgeLib.getPlatformId());
                syncToPlatform(journal, PMPHAppUtil.getCurrentUserEnName(), Const.SYNC_OPERATION_ADD);
            }
            // 如果非自动同步, 则修改同步状态为待同步;
            else {
                knowledgeSyncStatusService.update(Wrappers.lambdaUpdate(KnowledgeSyncStatus.class).set(KnowledgeSyncStatus::getKind, KnowledgeSyncStatus.FAILED_KIND_WAITING)
                        .eq(KnowledgeSyncStatus::getKnowledgeId, journal.getId())
                        .eq(KnowledgeSyncStatus::getPlatformId, platformKnowledgeLib.getPlatformId()));
            }
        }
    }

    public void updateArticleById(JournalArticle journalArticle) {
        LambdaUpdateWrapper<JournalArticle> updateWrapper = Wrappers.lambdaUpdate(JournalArticle.class)
                .set(JournalArticle::getBody, journalArticle.getBody())
                .eq(JournalArticle::getId, journalArticle.getId());
        journalArticleMapper.update(journalArticle, updateWrapper);
    }

    public Page<JournalArticle> queryArticleByJournalId(Page page, JournalArticle journalArticle) {
        LambdaQueryWrapper<JournalArticle> query = Wrappers.lambdaQuery(JournalArticle.class)
                .eq(JournalArticle::getJournalId, journalArticle.getJournalId())
                .like(StringUtils.isNotBlank(journalArticle.getName()), JournalArticle::getName, journalArticle.getName())
                .like(StringUtils.isNotBlank(journalArticle.getDoi()), JournalArticle::getDoi, journalArticle.getDoi())
                .like(StringUtils.isNotBlank(journalArticle.getAuthor()), JournalArticle::getAuthor, journalArticle.getAuthor())
                .like(StringUtils.isNotBlank(journalArticle.getClassify()), JournalArticle::getClassify, journalArticle.getClassify())
                .orderByAsc(JournalArticle::getId);
        return journalArticleMapper.selectPage(page, query);
    }

    public void revokePublish(String id) {
        Journal journal = journalMapper.selectById(id);
        journal.setType(Journal.TYPE_DRAFT);
        journalMapper.updateById(journal);
    }
}
